﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using Noris.Schedule.UI;
using Noris.Tools.DbManager.Components;

namespace Noris.Tools.DbManager.Reporting
{
	public class ReportEditor : Noris.Schedule.UI.Components.DblGraphControl
	{
		#region Konstrukce a obsluha vnitřních eventů
		public ReportEditor()
		{
            this.InitArea();
            this.InitScrollbars();
            this.InitMouse();
			this.Items = new List<ReportItem>();
            this.SizeChanged += new EventHandler(ReportEditor_SizeChanged);
		}

        void ReportEditor_SizeChanged(object sender, EventArgs e)
        {
            this.LayoutInnerDimensions();
			this.Draw();
        }
		#endregion

		public List<ReportItem> Items { get; private set; }

		#region Fyzické řízení kreslení
		protected override void OnPaintToBuffer(object sender, PaintEventArgs e)
		{
			base.OnPaintToBuffer(sender, e);

			this.PaintDrawBands(e);
			this.PaintDrawComponents(e);
           

			e.Graphics.ResetClip();
            this.HScr.Draw(e);
            this.VScr.Draw(e);
		}

		protected virtual void PaintDrawBands(PaintEventArgs e)
		{
			Rectangle area = Rectangle.Round(this.EditorBandArea);
			e.Graphics.SetClip(area);
			e.Graphics.FillRectangle(Brushes.LightGray, area);
		}
		protected virtual void PaintDrawComponents(PaintEventArgs e)
		{
			Rectangle area = Rectangle.Round(this.EditorComponentArea);
			e.Graphics.SetClip(area);
			e.Graphics.FillRectangle(Brushes.Azure, area);

			this.VisibleItems = new List<VisibleItem>();
			ElementPaintEventArgs args = new ElementPaintEventArgs(e, this.EditorBandArea, this.EditorComponentArea, this.GetVisualArea, this.GetLogicalArea);
			foreach (ReportItem item in Items)
			{
				args.Reset();
				item.Draw(this, args);
				if (args.IsVisible && args.IsInteractive)
					this.VisibleItems.AddRange(CreateVisibleList(args, item));
			}
		}
		#endregion
		#region Dokument (Data Object Model)
		/// <summary>
		/// Data editovaného dokumentu
		/// </summary>
		public ReportDocument Document { get { return _Document; } set { _Document = value; this.ReloadDocument(); } }

		protected void ReloadDocument()
		{
			
		}
		private ReportDocument _Document;
		#endregion
		#region After Resize
		/// <summary>
        /// Akceptuje vnější rozměry controlu, přizpůsobí jim vnitřní rozměry, ovlivní zobrazení Scrollbaru
        /// </summary>
        protected void LayoutInnerDimensions()
        {
            int sw = SystemInformation.VerticalScrollBarWidth;
            int sh = SystemInformation.HorizontalScrollBarHeight;
            RectangleF bounds = this.ClientArea;
            PointF origin = this.OriginVisualPoint;
            PointF compEnd = new PointF(bounds.Right - sw - 1f, bounds.Bottom - sh - 1f);      // Bod vpravo dole, kde bude končit prostor pro komponenty. Potkávají se tam ScrollBary.
            this.VScr.Bounds = new RectangleF(compEnd.X, origin.Y, sw, compEnd.Y - origin.Y);
            this.HScr.Bounds = new RectangleF(origin.X, compEnd.Y, compEnd.X - origin.X, sh);

            this.EditorBandArea = new RectangleF(bounds.X, origin.Y, origin.X - bounds.X, compEnd.Y - origin.Y);
            this.EditorComponentArea = new RectangleF(origin.X, origin.Y, compEnd.X - origin.X, compEnd.Y - origin.Y);
        }
        #endregion
        #region Visible elements
        /// <summary>
        /// Zjistí, zda změnou myši (pozice, stav) došlo k nějaké změně v oblasti Bands.
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
		protected void DetectChangeOnBand(EditorInteractiveEventArgs args)
		{
		}
        /// <summary>
        /// Zjistí, zda změnou myši (pozice, stav) došlo k nějaké změně v oblasti Components.
        /// Detekuje změnu prvku, který se nachází pod myší.
		/// Pokud se tam nachází stále totéž, vrací false (není třeba nic řešit).
		/// Pokud dojde ke změně, pak nový prvek (anebo null) vloží do this.ItemAtMouse
		/// </summary>
		/// <param name="point"></param>
		/// <returns></returns>
		protected void DetectChangeOnComponent(EditorInteractiveEventArgs args)
		{
			MouseChangeState requestChange = args.MouseChange;
			bool requestRun = true;
			ReportItem item = this.ItemAtMouse;
			if (args.MouseChange == MouseChangeState.Enter || args.MouseChange == MouseChangeState.Leave || args.MouseChange == MouseChangeState.LeftDown || args.MouseChange == MouseChangeState.Move || args.MouseChange == MouseChangeState.RightDown)
			{	// Pokud není myš stisknutá, najdu prvek, nad nímž se aktuálně pohybuje, nebo který aktuálně stiskla:
				VisibleItem vItem = this.FindItemAtPoint(args.MousePointF);
				item = (vItem == null ? null : vItem.Item);
			}
			if (!Object.ReferenceEquals(item, this.ItemAtMouse))
			{	// Změna prvku pod myší:

				// Starý prvek opouštíme:
				if (this.ItemAtMouse != null)
				{
					args.MouseChange = MouseChangeState.Leave;
					this.ItemAtMouse.DetectChangeOnMouseChange(args);
					if (requestChange == MouseChangeState.Leave)
						requestRun = false;
				}

				// Na nový prvek vstupujeme:
				this.ItemAtMouse = item;
				if (this.ItemAtMouse != null)
				{
					args.MouseChange = MouseChangeState.Enter;
					this.ItemAtMouse.DetectChangeOnMouseChange(args);
					if (requestChange == MouseChangeState.Enter)
						requestRun = false;
				}
			}

			// Požadovaná akce:
			if (requestRun && this.ItemAtMouse != null)
			{
				args.MouseChange = requestChange;
				this.ItemAtMouse.DetectChangeOnMouseChange(args);
			}
		}
		/// <summary>
		/// Vytvoří a vrátí seznam s prvky VisibleItem vytvořený pro aktivní prostory uvedené v argumentu args pro daný item.
		/// </summary>
		/// <param name="args"></param>
		/// <param name="item"></param>
		/// <returns></returns>
		protected List<VisibleItem> CreateVisibleList(ElementPaintEventArgs args, ReportItem item)
		{
			List<VisibleItem> result = new List<VisibleItem>();
			foreach (RectangleF area in args.ActiveVisualAreaList)
				result.Add(new VisibleItem(area, item));
			return result;
		}
		/// <summary>
		/// Najde a vrátí prvek, který se nachází pod myší.
		/// Pokud je jich na sobě v daném místě víc, vrátí ten, který je navrchu = je viditelný.
		/// Pokud není žádný, vrací null.
		/// </summary>
		/// <param name="point">Bod ve fyzických souřadnicích this Controlu = typicky MouseEventArgs.Location</param>
		/// <returns></returns>
		protected VisibleItem FindItemAtPoint(PointF point)
		{
			for (int i = this.VisibleItems.Count - 1; i >= 0; i--)
			{
				if (this.VisibleItems[i].Bounds.Contains(point))
					return this.VisibleItems[i];
			}
			return null;
		}
		protected List<VisibleItem> VisibleItems;
		/// <summary>
		/// Prvek, který se nachází pod myší.
		/// Anebo 
		/// </summary>
		protected ReportItem ItemAtMouse { get; set; }
		protected class VisibleItem
		{
			public VisibleItem(RectangleF bounds, ReportItem item)
			{
				this.Bounds = bounds;
				this.Item = item;
			}
			internal RectangleF Bounds { get; private set; }
			internal ReportItem Item { get; private set; }
		}
		#endregion
        #region Scrollbary
        /// <summary>
        /// Inicializuje Scrollbary
        /// </summary>
        protected void InitScrollbars()
        {
            this.HScr = new ScrollBar();
            using (this.HScr.SuppresRecalculate())
            {
                this.HScr.Bounds = new RectangleF(35, 303, 450, 15);
                this.HScr.Minimum = 0;
                this.HScr.Maximum = 210;
                this.HScr.Showing = 50;
				this.HScr.ValueChanged += new EventHandler<ValueFloatChangedEventArgs>(HVScr_ValueChanged);
            }

            this.VScr = new ScrollBar();
            using (this.VScr.SuppresRecalculate())
            {
                this.VScr.Bounds = new RectangleF(485, 3, 15, 300);
                this.VScr.Minimum = 0;
                this.VScr.Maximum = 297;
                this.VScr.Showing = 50;
				this.VScr.ValueChanged += new EventHandler<ValueFloatChangedEventArgs>(HVScr_ValueChanged);
            }
        }
		/// <summary>
		/// Handler události po změně hodnoty
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void HVScr_ValueChanged(object sender, ValueFloatChangedEventArgs e)
		{
			this.OriginLogicalPoint = new PointF(this.HScr.CurrentBegin, this.VScr.CurrentBegin);
		}
        /// <summary>
        /// Detekuje, zda pohybem myši nad daným scrollbarem došlo ke změně vizuálního stavu.
        /// </summary>
        /// <param name="scrollBar"></param>
        /// <param name="point">Bod, na němž je nyní myš (kromě stavu Leave)</param>
		/// <param name="mouseState">Dynamický stav myši (popisuje i změny stavu)</param>
        /// <returns></returns>
		private void DetectChangeOnScrollbar(ScrollBar scrollBar, EditorInteractiveEventArgs args)
        {
            scrollBar.DetectChangeOnMouseChange(args);
        }
        protected ScrollBar VScr;
        protected ScrollBar HScr;
        #endregion
        #region Práce s myší
        /// <summary>
        /// Inicializace proměnných myši
        /// </summary>
        protected void InitMouse()
        {
            this.CurrentAreaType = InnerAreaType.None;
			this.CurrentPressedButton = MouseButtons.None;
			this.MouseEnter += new EventHandler(ReportEditor_MouseEnter);
			this.MouseMove += new MouseEventHandler(ReportEditor_MouseMove);
			this.MouseDown += new MouseEventHandler(ReportEditor_MouseDown);
			this.MouseUp += new MouseEventHandler(ReportEditor_MouseUp);
			this.MouseLeave += new EventHandler(ReportEditor_MouseLeave);
		}
		void ReportEditor_MouseEnter(object sender, EventArgs e)
		{
		}
		void ReportEditor_MouseMove(object sender, MouseEventArgs e)
		{
			if (e.Button == System.Windows.Forms.MouseButtons.None)
				this.ReportEditor_MouseMoveNone(sender, e);
			else if (e.Button == System.Windows.Forms.MouseButtons.Left)
				this.ReportEditor_MouseMoveLeft(sender, e);
		}
		/// <summary>
		/// Pohyb myši - bez tlačítka
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ReportEditor_MouseMoveNone(object sender, MouseEventArgs e)
		{
			EditorInteractiveEventArgs args = GetInteractiveArgs(e.Location, MouseChangeState.None);
			InnerAreaType newAreaType = this.FindAreaType(e.Location);
			if (newAreaType != this.CurrentAreaType)
			{
				args.MouseChange = MouseChangeState.Leave;
				this.ReportEditor_MouseChange(this.CurrentAreaType, args);

				this.CurrentAreaType = newAreaType;
				args.MouseChange = MouseChangeState.Enter;
				this.ReportEditor_MouseChange(this.CurrentAreaType, args);
			}
			args.MouseChange = MouseChangeState.Move;
			this.ReportEditor_MouseChange(this.CurrentAreaType, args);

			this.ProcessInteractiveArgsResult(args);
		}
		/// <summary>
		/// Pohyb myši - se stisknutým levým tlačítkem (tažení)
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ReportEditor_MouseMoveLeft(object sender, MouseEventArgs e)
		{   // Zde nemá být detekce prostoru (this.CurrentAreaType), 
			//   protože tažení pomocí myši se týká již zmáčknutého prvku, 
			//   bez ohledu na to, zda se myš už nachází nad jiným prvkem.
			// Pokud je neprázdný Fender, a aktuální bod je uvnitř, pak Drag ještě nezačíná:
			if (!this.MouseDragFender.IsEmpty && this.MouseDragFender.Contains(e.Location))
				return;
			this.MouseDragFender = Rectangle.Empty;    // Hodnota Empty bude v příštím běhu příznakem, že se už Fender nedetekuje (Drag už byl zahájen).

			EditorInteractiveEventArgs args = GetInteractiveArgs(e.Location, MouseChangeState.LeftDrag);
			this.ReportEditor_MouseChange(this.CurrentAreaType, args);
			this.ProcessInteractiveArgsResult(args);
		}
		void ReportEditor_MouseDown(object sender, MouseEventArgs e)
		{
			if (e.Button == System.Windows.Forms.MouseButtons.Left)
				this.ReportEditor_MouseDownLeft(sender, e);
			else if (e.Button == System.Windows.Forms.MouseButtons.Right)
				this.ReportEditor_MouseDownRight(sender, e);
		}
		/// <summary>
		/// Zmáčknutí levého tlačítka myši
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ReportEditor_MouseDownLeft(object sender, MouseEventArgs e)
		{
			this.MouseDragFender = GetDragFender(e.Location);
			this.MouseDownPoint = e.Location;
			this.CurrentPressedButton = System.Windows.Forms.MouseButtons.Left;
			this.CurrentAreaType = this.FindAreaType(e.Location);
			EditorInteractiveEventArgs args = GetInteractiveArgs(e.Location, MouseChangeState.LeftDown);
			this.ReportEditor_MouseChange(this.CurrentAreaType, args);
			this.ProcessInteractiveArgsResult(args);
		}
		/// <summary>
		/// Zmáčknutí pravého tlačítka myši
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ReportEditor_MouseDownRight(object sender, MouseEventArgs e)
		{
			this.MouseDragFender = GetDragFender(e.Location);
			this.MouseDownPoint = e.Location;
			this.CurrentPressedButton = System.Windows.Forms.MouseButtons.Right;
			this.CurrentAreaType = this.FindAreaType(e.Location);
			EditorInteractiveEventArgs args = GetInteractiveArgs(e.Location, MouseChangeState.RightDown);
			this.ReportEditor_MouseChange(this.CurrentAreaType, args);
			this.ProcessInteractiveArgsResult(args);
		}
		/// <summary>
		/// Zvednutí myši
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void ReportEditor_MouseUp(object sender, MouseEventArgs e)
		{
			if (this.CurrentPressedButton == System.Windows.Forms.MouseButtons.Left)
				this.ReportEditor_MouseUpLeft(sender, e);
			else if (this.CurrentPressedButton == System.Windows.Forms.MouseButtons.Right)
				this.ReportEditor_MouseUpRight(sender, e);
		}
		/// <summary>
		/// Uvolnění (konec zmáčknutí) levého tlačítka myši
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ReportEditor_MouseUpLeft(object sender, MouseEventArgs e)
		{
			EditorInteractiveEventArgs args = GetInteractiveArgs(e.Location, MouseChangeState.LeftUp);
			this.ReportEditor_MouseChange(this.CurrentAreaType, args);
			this.ProcessInteractiveArgsResult(args);
			this.CurrentPressedButton = System.Windows.Forms.MouseButtons.None;
		}
		/// <summary>
		/// Uvolnění (konec zmáčknutí) pravého tlačítka myši
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ReportEditor_MouseUpRight(object sender, MouseEventArgs e)
		{
			EditorInteractiveEventArgs args = GetInteractiveArgs(e.Location, MouseChangeState.RightUp);
			this.ReportEditor_MouseChange(this.CurrentAreaType, args);
			this.ProcessInteractiveArgsResult(args);
			this.CurrentPressedButton = System.Windows.Forms.MouseButtons.None;
		}
		/// <summary>
		/// Myš opouští plochu
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void ReportEditor_MouseLeave(object sender, EventArgs e)
		{
			EditorInteractiveEventArgs args = GetInteractiveArgs(Point.Empty, MouseChangeState.Leave);
			this.ReportEditor_MouseChange(this.CurrentAreaType, args);
			this.CurrentAreaType = InnerAreaType.None;
			this.ProcessInteractiveArgsResult(args);
		}
		/// <summary>
		/// Obecná metoda: zjistí, zda konkrétní změna myši v dané oblasti 
		/// (pohyb, stisknutí / uvolnění tlačítka) má jako důsledek potřebu překreslení Controlu
		/// </summary>
		/// <param name="areaType"></param>
		/// <param name="point"></param>
		/// <param name="mouseChange"></param>
		/// <returns></returns>
		protected void ReportEditor_MouseChange(InnerAreaType areaType, EditorInteractiveEventArgs args)
		{
			switch (this.CurrentAreaType)
			{
				case InnerAreaType.Bands:
					this.DetectChangeOnBand(args);
					break;
				case InnerAreaType.Components:
					this.DetectChangeOnComponent(args);
					break;
				case InnerAreaType.VScr:
					this.DetectChangeOnScrollbar(this.VScr, args);
					break;
				case InnerAreaType.HScr:
					this.DetectChangeOnScrollbar(this.HScr, args);
					break;
			}
		}
		/// <summary>
		/// Vrátí argument EditorInteractiveEventArgs pro daný bod a druh stavu, do argumentu přidá pointery na metody kalkulací
		/// </summary>
		/// <param name="mousePointF"></param>
		/// <param name="mouseChange"></param>
		/// <returns></returns>
		protected EditorInteractiveEventArgs GetInteractiveArgs(PointF mousePointF, MouseChangeState mouseChange)
		{
			return new EditorInteractiveEventArgs(mousePointF, mouseChange, this.GetVisualArea, this.GetLogicalArea);
		}
		/// <summary>
		/// Zpracuje výstupní požadavky uložené v args: NeedDraw a NeedCursor
		/// </summary>
		/// <param name="args"></param>
		protected void ProcessInteractiveArgsResult(EditorInteractiveEventArgs args)
		{
			if (args.NeedDraw)
				this.Draw();
			if (args.NeedCursor != SysCursorType.No)
				this.Cursor = SysCursors.GetCursor(args.NeedCursor);
		}
		/// <summary>
        /// Najde a vrátí typ prostoru pro danou souřadnici myši
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        protected InnerAreaType FindAreaType(PointF point)
        {
            if (this.EditorBandArea.Contains(point)) return InnerAreaType.Bands;
            if (this.EditorComponentArea.Contains(point)) return InnerAreaType.Components;
            if (this.VScr.Bounds.Contains(point)) return InnerAreaType.VScr;
            if (this.HScr.Bounds.Contains(point)) return InnerAreaType.HScr;
            return InnerAreaType.None;
        }
		/// <summary>
		/// Vrátí souřadnice prostoru, v němž se bude ignorovat pohyb myši před zahájením Drag
		/// </summary>
		/// <param name="point"></param>
		/// <returns></returns>
		private Rectangle GetDragFender(Point point)
		{
			Size fender = SystemInformation.DragSize;
			return new Rectangle(new Point(point.X - fender.Width / 2, point.Y - fender.Height / 2), fender);
		}
        /// <summary>
        /// Aktuální prostor pod myší
        /// </summary>
        protected InnerAreaType CurrentAreaType;
		/// <summary>
		/// Aktuálně zmáčknuté tlačítko myši (v eventu MouseUp zde zjistíme, které bylo zmáčknuté před tím)
		/// </summary>
		protected MouseButtons CurrentPressedButton;
		/// <summary>
		/// Souřadnice, na které byla zmáčknuta myš.
		/// Koordináty: this Control, vizuální jednotky (pixel).
		/// </summary>
		protected Point MouseDownPoint;
		/// <summary>
		/// Rámeček okolo bodu, kde se zmáčkla myš.
		/// Koordináty: this Control, vizuální jednotky (pixel).
		/// Uvnitř tohoto rámečku se pohyb zmáčknuté myši ignoruje (tolerance chvění).
		/// Pokud se zmáčknutá myš bude pohybovat mimo tento rámeček, bude to prezentováno jak Tažení (Drag).
		/// </summary>
		protected Rectangle MouseDragFender;
        /// <summary>
        /// Prostory s různým obsazením a různým chováním
        /// </summary>
        protected enum InnerAreaType
        {
            None,
            Bands,
            Components,
            VScr,
            HScr
        }
        #endregion
        #region Virtuální plocha - řeší Zoom a Shift prvků po viditelné části controlu (editační plocha)
        /// <summary>
        /// Inicializuje virtuální plochu
        /// </summary>
        protected void InitArea()
        {
            this.ZoomRatio = 1f / 0.28f;                  // Typická rozteč pixelu je 0.28 mm;
            this.OriginVisualPoint = new Point(68, 12);   // Rozmístění pravítka a Bounds
        }
        /// <summary>
        /// Vypočítá a vrátí vizuální souřadnice (v pixelech) 
		/// pro dané logické souřadnice pro prvek daného typu (Band / Component).
        /// Vrácené souřadnice mohou ležet mimo viditelnou plochu (this.EditorBandArea), o tom informuje out parametr isVisible
        /// </summary>
        /// <param name="logicalArea">Vstupní logické souřadnice</param>
		/// <param name="areaType">Druh prostoru, podle něj se vybírá přepočtový mechanismus</param>
		/// <param name="isVisible">Out informace o tom, zda výstupní souřadnice jsou alespoň částečně ve viditelné oblasti this.EditorBandArea</param>
		/// <returns>Výstupní souřadnice (reciproční ke vstupním)</returns>
		protected RectangleF GetVisualArea(RectangleF logicalArea, CalculateAreaType areaType, out bool isVisible)
        {
			switch (areaType)
			{
				case CalculateAreaType.Band:
					return this.GetVisualArea(logicalArea, this.EditorBandArea, true, out isVisible);
				case CalculateAreaType.Component:
					return this.GetVisualArea(logicalArea, this.EditorComponentArea, false, out isVisible);
			}
			isVisible = false;
			return RectangleF.Empty;
        }
        /// <summary>
        /// Vypočítá a vrátí vizuální souřadnice (v pixelech) pro dané logické souřadnice (logicalArea) umístěné do prostoru daných fyzických souřadnic (visualArea).
        /// Volitelně umožňuje, aby vracené souřadnice měly X a Width shodné s fyzickými souřadnicemi (visualArea), což se používá pro typ Bands.
        /// </summary>
        /// <param name="logicalArea">Vstupní logické souřadnice</param>
        /// <param name="editorArea">Vizuální prostor v pixelech, do něhož se logické souřadnice transformují</param>
        /// <param name="wholeWidth">Požadavek (true) na výpočet souřadnice X a Width = visualArea; false = standardní přepočet</param>
        /// <returns></returns>
        protected RectangleF GetVisualArea(RectangleF logicalArea, RectangleF editorArea, bool wholeWidth, out bool isVisible)
        {
            PointF origin = this.OriginLogicalPoint;
            float ratio = this.ZoomRatio;
            float logOfsX = (wholeWidth ? 0f : logicalArea.X - origin.X); // Vzdálenost počátku dané souřadnice od bodu, který reprezentuje logickou souřadnici počátku viditelné oblasti (X, Y), v logických souřadnicích
            float logOfsY = logicalArea.Y - origin.Y;
            float fyzOfsX = (wholeWidth ? 0f : logOfsX * ratio);          // Vzdálenost počátku dané souřadnice v pixelech od viditelného počátku (X, Y), v pixelech
            float fyzOfsY = logOfsY * ratio;
            float fyzWidt = (wholeWidth ? editorArea.Width : logicalArea.Width * ratio);  // Rozměry v pixelech (W, H)
            float fyzHeig = logicalArea.Height * ratio;
            RectangleF result = new RectangleF(editorArea.X + fyzOfsX, editorArea.Y + fyzOfsY, fyzWidt, fyzHeig);
            isVisible = editorArea.IntersectsWith(result);
            return result;
        }
		/// <summary>
		/// Vypočítá a vrátí logické souřadnice (v logických jednotkách, milimetry) 
		/// pro dané vizuální souřadnice pro prvek daného typu (Band / Component).
		/// Vrácené souřadnice mohou ležet mimo viditelnou plochu (this.EditorBandArea), o tom informuje out parametr isVisible
		/// </summary>
		/// <param name="logicalArea">Vstupní logické souřadnice</param>
		/// <param name="areaType">Druh prostoru, podle něj se vybírá přepočtový mechanismus</param>
		/// <param name="isVisible">Out informace o tom, zda výstupní souřadnice jsou alespoň částečně ve viditelné oblasti this.EditorBandArea</param>
		/// <returns>Výstupní souřadnice (reciproční ke vstupním)</returns>
		protected RectangleF GetLogicalArea(RectangleF visualArea, CalculateAreaType areaType, out bool isVisible)
		{
			switch (areaType)
			{
				case CalculateAreaType.Band:
					return this.GetLogicalArea(visualArea, this.EditorBandArea, true, out isVisible);
				case CalculateAreaType.Component:
					return this.GetLogicalArea(visualArea, this.EditorComponentArea, false, out isVisible);
			}
			isVisible = false;
			return RectangleF.Empty;
		}
		/// <summary>
		/// Vypočítá a vrátí vizuální souřadnice (v pixelech) pro dané logické souřadnice (logicalArea) umístěné do prostoru daných fyzických souřadnic (visualArea).
		/// Volitelně umožňuje, aby vracené souřadnice měly X a Width shodné s fyzickými souřadnicemi (visualArea), což se používá pro typ Bands.
		/// </summary>
		/// <param name="visualArea">Vstupní logické souřadnice</param>
		/// <param name="editorArea">Vizuální prostor v pixelech, do něhož se logické souřadnice transformují</param>
		/// <param name="wholeWidth">Požadavek (true) na výpočet souřadnice X a Width = visualArea; false = standardní přepočet</param>
		/// <returns></returns>
		protected RectangleF GetLogicalArea(RectangleF visualArea, RectangleF editorArea, bool wholeWidth, out bool isVisible)
		{
			PointF origin = this.OriginLogicalPoint;
			float ratio = this.ZoomRatio;
			float logX = (visualArea.X - editorArea.X) / ratio + origin.X;
			float logY = (visualArea.Y - editorArea.Y) / ratio + origin.Y;
			float logW = visualArea.Width / ratio;
			float logH = visualArea.Height / ratio;
			isVisible = true;
			return new RectangleF(logX, logY, logW, logH);
		}
        /// <summary>
		/// Ratio přepočtu rozměrů = Zoomování.
        /// Vyjadřuje počet pixelů, které odpovídají jednomu milimetru.
		/// Tímto koeficientem se přepočítává logický rozměr prvků (kde jednotka = 1 mm) na pixely. Hodnota blížící se k 0 : maličké prvky.
		/// </summary>
		protected float ZoomRatio { get; set; }
        /// <summary>
        /// Souřadnice vizuálního bodu (v pixelech), na němž začíná editační ploocha prvků (this.EditorComponentArea).
        /// Souřadnice X odpovídá předělu mezi Bounds a Components, 
        /// souřadnice Y pak nechává nahoře prostor pro pravítko (ToFuture).
        /// Tento bod se nemění při Resize controlu.
        /// </summary>
        protected Point OriginVisualPoint { get; set; }
        /// <summary>
		/// Souřadnice logického bodu (v jednotkách [mm]), který se bude zobrazovat v bodě (0,0) editační plochy prvků (this.EditorItemArea).
		/// </summary>
		protected PointF OriginLogicalPoint { get; set; }
		/// <summary>
		/// Oblast editoru, která je vyhrazena k zobrazování záhlaví svazků (Band).
		/// Jednotkou jsou fyzické pixely.
		/// Celou šířku nechť vyplní element Band. Tato šířka se nescrolluje.
		/// Výška je daná součtem výšky Band, scrolluje se s osou Y, reaguje na OriginLogicalPoint.
		/// </summary>
		protected RectangleF EditorBandArea { get; set; }
		/// <summary>
		/// Oblast editoru, která je vyhrazena k zobrazování prvků (Component).
		/// Jednotkou jsou fyzické pixely.
		/// Jednotlivé prvky zde scrollují v ose X i Y.
		/// </summary>
		protected RectangleF EditorComponentArea { get; set; }
		#endregion
	}
    #region class ElementPaintEventArgs : data pro kreslení elementů (do metody EditorElement.Draw)
    /// <summary>
	/// ElementPaintEventArgs : data pro kreslení elementů (do metody EditorElement.Draw)
	/// </summary>
	public class ElementPaintEventArgs : EditorEventArgs, IDisposable
	{
        internal ElementPaintEventArgs(PaintEventArgs innerArgs, RectangleF editorBandArea, RectangleF editorItemArea, CalculateArea getBandVisualArea, CalculateArea getComponentVisualArea)
			: base(getBandVisualArea, getComponentVisualArea)
        {
            this._InnerArgs = innerArgs;
            this.EditorBandArea = editorBandArea;
            this.EditorItemArea = editorItemArea;
        }
		private PaintEventArgs _InnerArgs;
		void IDisposable.Dispose()
		{
            this._InnerArgs = null;
		}
        /// <summary>
        /// Resetuje objekt před jeho předáním do metody Draw().
        /// Objekt se používá jeden pro kreslení všech prvků v jednom cyklu.
        /// </summary>
        internal void Reset()
        {
            this.IsVisible = false;
            this.IsInteractive = false;
			this.ActiveVisualAreaList = new List<RectangleF>();
        }
        /// <summary>
        /// PaintEventArgs.ClipRectangle: Gets the rectangle in which to paint.
        /// </summary>
        /// <value>The System.Drawing.Rectangle in which to paint.</value>
        public Rectangle ClipRectangle { get { return this._InnerArgs.ClipRectangle; } }
        /// <summary>
        /// PaintEventArgs.Graphics: Gets the graphics used to paint.
        /// </summary>
        /// <value>The System.Drawing.Graphics object used to paint. The System.Drawing.Graphics object provides methods for drawing objects on the display device.</value>
        public Graphics Graphics { get { return this._InnerArgs.Graphics; } }
		/// <summary>
		/// Oblast editoru, která je vyhrazena k zobrazování záhlaví svazků (Band).
		/// Jednotkou jsou fyzické pixely.
		/// Celou šířku nechť vyplní element Band. Tato šířka se nescrolluje.
		/// Výška je daná součtem výšky Band, scrolluje se s osou Y, reaguje na OriginLogicalPoint.
		/// </summary>
		public RectangleF EditorBandArea { get; private set; }
		/// <summary>
		/// Oblast editoru, která je vyhrazena k zobrazování prvků (Item).
		/// Jednotkou jsou fyzické pixely.
		/// Jednotlivé prvky zde scrollují v ose X i Y.
		/// </summary>
		public RectangleF EditorItemArea { get; private set; }
        /// <summary>
        /// Zde element nastavuje příznak, zda byl fyzicky vykreslen (true) nebo ne (false).
        /// To záleží na jeho stavu a na jeho pozici, přepočtené přes měřítko, zda se nachází ve viditelném rámci.
        /// Výchozí hodnota je false = nevykresleno.
        /// Nevykreslené objekty se nebudou zahrnovat do interaktivních dějů v editoru.
        /// </summary>
        public bool IsVisible { get; set; }
        /// <summary>
        /// Zde element nastavuje příznak, zda je interaktivní = zda se s ním má počítat při pohybech myši (true) nebo ne (false).
        /// To záleží na jeho typu: neinteraktivní prvek je pouze pozadí Band v ploše Components (tj. pozadí v editační ploše).
        /// Vše ostatní je jistým způsobem interaktivní, a to i Fixed komponenty (nepohyblivé) = mohou mít svoje kontextové menu a okno s detailem.
        /// Výchozí hodnota je false = neinteraktivní.
        /// Neinteraktivní objekty se nebudou zahrnovat do interaktivních dějů v editoru.
        /// </summary>
        public bool IsInteractive { get; set; }
        /// <summary>
		/// Seznam souřadnic (Rectangles), na kterých se nachází tento element.
		/// Element může zabírat více než jeden RectangleF, v jehož rámci má být aktivní.
		/// Typicky v klidu zabírá jeden RectangleF, ale pokud má vykresleny hranové a vrcholové úchytky, pak se o ně zvětšuje jeho aktivní prostor.
        /// Pokud je v IsVisible = false, pak je zde prázdný List.
        /// </summary>
        public List<RectangleF> ActiveVisualAreaList { get; set; }

	}
	#endregion
	#region class EditorInteractiveEventArgs : data pro řízení interaktivity objektů
	/// <summary>
	/// EditorInteractiveEventArgs : data pro řízení interaktivity objektů
	/// </summary>
	public class EditorInteractiveEventArgs : EditorEventArgs
	{
		internal EditorInteractiveEventArgs(PointF mousePointF, MouseChangeState mouseChange, CalculateArea getBandVisualArea, CalculateArea getComponentVisualArea)
			: base(getBandVisualArea, getComponentVisualArea)
		{
			this.MousePointF = mousePointF;
			this.MouseChange = mouseChange;
			this.NeedDraw = false;
			this.NeedCursor = SysCursorType.None;
		}
		/// <summary>
		/// Bod, na kterém se nachází myš.
		/// Koordináty = pixely controlu
		/// </summary>
		public PointF MousePointF { get; private set; }
		/// <summary>
		/// Stav myši nebo změna stavu
		/// </summary>
		public MouseChangeState MouseChange { get; internal set; }
		/// <summary>
		/// Out: akce myši vyžaduje Draw controlu.
		/// Jde o střadač hodnoty true (první nastavení true vloží true, následné vložení false už nemá vliv: zůstává uloženo true).
		/// </summary>
		public bool NeedDraw { get { return this._NeedDraw; } set { if (value) this._NeedDraw = true; } }
		private bool _NeedDraw;
		/// <summary>
		/// Out: aktuálně platný kurzor. Pamatuje si posledně vloženou hodnotu, neimplementuje žádnou přidanou logiku.
		/// </summary>
		public SysCursorType NeedCursor { get; set; }
	}
	#endregion
	#region class EditorEventArgs : Bázová třída argumentů pro události v rámci editoru;   delegate CalculateArea : přepočty souřadnic;   enum CalculateAreaType
	/// <summary>
	/// EditorEventArgs : Bázová třída argumentů pro události v rámci editoru
	/// </summary>
	public class EditorEventArgs : EventArgs, IDisposable
	{
		public EditorEventArgs(CalculateArea getVisualArea, CalculateArea getLogicalArea)
		{
			this.GetVisualArea = getVisualArea;
			this.GetLogicalArea = getLogicalArea;
		}
		/// <summary>
		/// Pointer na metodu, která vrátí vizuální souřadnice pro logické souřadnice editovaného prvku
		/// </summary>
		public CalculateArea GetVisualArea { get; private set; }
		/// <summary>
		/// Pointer na metodu, která vrátí logické souřadnice pro vizuální souřadnice editovaného prvku
		/// </summary>
		public CalculateArea GetLogicalArea { get; private set; }
		void IDisposable.Dispose()
		{
			this.GetVisualArea = null;
			this.GetLogicalArea = null;
		}
	}
	/// <summary>
	/// Předpis pro metodu, která přepočítává souřadnice (RectangleF) a navíc nastavuje do out parametru informaci o viditelnosti
	/// </summary>
	/// <param name="inputArea">Vstupní souřadnice (logické / vizuální)</param>
	/// <param name="areaType">Druh prostoru, podle něj se vybírá přepočtový mechanismus</param>
	/// <param name="isVisible">Výstup: jsou ve viditelné oblasti</param>
	/// <returns>Výstupní souřadnice (reciproční ke vstupním)</returns>
	public delegate RectangleF CalculateArea(RectangleF inputArea, CalculateAreaType areaType, out bool isVisible);
	/// <summary>
	/// Druh objektu k přepočtu
	/// </summary>
	public enum CalculateAreaType
	{
		/// <summary>
		/// Neurčeno
		/// </summary>
		None = 0,
		/// <summary>
		/// Band = záhlaví svazku
		/// </summary>
		Band,
		/// <summary>
		/// Component = prvky (jednotlivé položky sestavy)
		/// </summary>
		Component
	}
	#endregion
}
